package util;

import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLTextExtractor;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.io.*;
import java.util.*;

/**
 * @author 别人
 */
public class WordUtils {

    public static String readDoc(String filePath) {
        String text = "";
        // 判断文件是否是docx
        try {
            if (filePath.endsWith(".doc")) {
                try (FileInputStream fis = new FileInputStream(filePath); HWPFDocument doc = new HWPFDocument(fis); WordExtractor extractor = new WordExtractor(doc)) {
                    text = extractor.getText();
                }
            } else if (filePath.endsWith(".docx")) {
                try (OPCPackage opcPackage = POIXMLDocument.openPackage(filePath); POIXMLTextExtractor extractor = new XWPFWordExtractor(opcPackage)) {
                    text = extractor.getText();
                }
            } else {
                throw new RuntimeException("目标文件并不是word文档");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return text;
    }


    private static void handleMerge(List<OPCPackage> opcpList, OutputStream dest) {

        int opcpSize = opcpList.size();
        //获取的OPCPackage对象大于0时，执行合并操作
        if (opcpSize > 0) {
            try {
                XWPFDocument src1Document = new XWPFDocument(opcpList.get(0));
                CTBody src1Body = src1Document.getDocument().getBody();
                //OPCPackage大于1的部分执行合并操作
                if (opcpSize > 1) {
                    for (int i = 1; i < opcpSize; i++) {
                        OPCPackage src2Package = opcpList.get(i);
                        XWPFDocument src2Document = new XWPFDocument(src2Package);
                        CTBody src2Body = src2Document.getDocument().getBody();
                        appendBody(src1Body, src2Body);
                    }
                }
                //将合并的文档写入目标文件中
                src1Document.write(dest);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 注释掉以下部分，去除影响目标文件srcDocxs。(TIPS: 原因不明)
                /*
                  for (OPCPackage opcPackage : opcpList) {
                      if (null != opcPackage) {
                          try {
                              opcPackage.close();
                          } catch (IOException e) {
                              e.printStackTrace();
                          }
                      }
                  }
                */
                // 关闭流
                IOUtils.closeQuietly(dest);
            }
        }
    }

    public static void mergeDoc(String[] srcDocxs, String destDocx) {
        mergeDoc(Arrays.asList(srcDocxs), destDocx);
    }

    public static void mergeDoc(List<byte[]> docxs, OutputStream dest) {
        List<OPCPackage> opcpList = changeToOPCPackage(docxs);
        handleMerge(opcpList, dest);
    }

    /**
     * 合并两个docx文档，但是对文档包含的图片无效
     */
    public static void mergeDoc(List<String> srcDocxs, String destDocx) {
        List<OPCPackage> opcpList = changeToOPCPackage(srcDocxs);
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(destDocx);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        handleMerge(opcpList, outputStream);
    }

    private static List<OPCPackage> changeToOPCPackage(List<?> srcDocxs) {
        List<OPCPackage> opcpList = new ArrayList<>();
        for (Object srcDocx : srcDocxs) {
            OPCPackage srcPackage = null;
            Class<?> aClass = srcDocx.getClass();
            if (aClass.isArray() && aClass.getComponentType().equals(byte.class)) {
                InputStream docxIs = new ByteArrayInputStream((byte[]) srcDocx);
                try {
                    srcPackage = OPCPackage.open(docxIs);
                } catch (InvalidFormatException | IOException e) {
                    e.printStackTrace();
                }
            } else if (aClass.equals(String.class)) {
                try {
                    srcPackage = OPCPackage.open((String) srcDocx);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                System.err.println("提供了不支持的类型");
            }
            if (null != srcPackage) {
                opcpList.add(srcPackage);
            }
        }
        return opcpList;
    }


        /*
        public static void downloadDocFile(String title, List<byte[]> docxs, HttpServletResponse response) {
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            mergeDoc(docxs, os);
            byte[] content = os.toByteArray();
            System.out.println("byte[] content:" + content.length);
            InputStream is = new ByteArrayInputStream(content);
            // 设置response参数，可以打开下载页面
            response.reset();
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + new String((title + ".docx").getBytes(), "iso-8859-1"));
            response.setContentLength(content.length);
            ServletOutputStream outputStream = response.getOutputStream();
            BufferedInputStream bis = new BufferedInputStream(is);
            BufferedOutputStream bos = new BufferedOutputStream(outputStream);
            byte[] buff = new byte[8192];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
            bis.close();
            bos.close();
            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    */

    /**
     * 合并文档内容
     *
     * @param src    目标文档
     * @param append 要合并的文档
     */
    private static void appendBody(CTBody src, CTBody append) throws Exception {
        XmlOptions optionsOuter = new XmlOptions();
        optionsOuter.setSaveOuter();
        String appendString = append.xmlText(optionsOuter);
        String srcString = src.xmlText();
        String prefix = srcString.substring(0, srcString.indexOf(">") + 1);
        String mainPart = srcString.substring(srcString.indexOf(">") + 1,
                srcString.lastIndexOf("<"));
        String suffix = srcString.substring(srcString.lastIndexOf("<"));
        String addPart = appendString.substring(appendString.indexOf(">") + 1,
                appendString.lastIndexOf("<"));
        CTBody makeBody = CTBody.Factory.parse(prefix + mainPart + addPart + suffix);
        src.set(makeBody);
    }


    /**
     * 将一个 Word 文档中的指定内容批量替换成指定内容，并返回替换后的 Word 文件流。
     *
     * @param params 要替换的文本内容
     */
    public ByteArrayOutputStream convertWordByXwpf(XWPFDocument document, Map<String, String> params) throws Exception {
        List<XWPFParagraph> xwpfParagraphList = document.getParagraphs();
        for (int i = 0, size = xwpfParagraphList.size(); i < size; i++) {
            XWPFParagraph xwpfParagraph = xwpfParagraphList.get(i);
            for (String key : params.keySet()) {
                if (xwpfParagraph.getText().contains(key))
                    replaceInParagraph(xwpfParagraph, key, params.get(key));
            }
        }
        Iterator<XWPFTable> tablesIterator = document.getTablesIterator();
        while (tablesIterator.hasNext()) {
            XWPFTable xwpfTable = tablesIterator.next();
            for (int i = 0, count = xwpfTable.getNumberOfRows(); i < count; i++) {
                XWPFTableRow xwpfTableRow = xwpfTable.getRow(i);
                List<XWPFTableCell> xwpfTableCellList = xwpfTableRow.getTableCells();
                for (int j = 0, cellSize = xwpfTableCellList.size(); j < cellSize; j++) {
                    XWPFTableCell xwpfTableCell = xwpfTableCellList.get(j);
                    List<XWPFParagraph> paragraphList = xwpfTableCell.getParagraphs();
                    for (int k = 0, paragraphSize = paragraphList.size(); k < paragraphSize; k++) {
                        XWPFParagraph xwpfParagraph = paragraphList.get(k);
                        if (this.isBlank(xwpfParagraph.getText())) continue;
                        for (String key : params.keySet()) {
                            if (xwpfParagraph.getText().contains(key))
                                replaceInParagraph(xwpfParagraph, key, params.get(key));
                        }
                    }
                }
            }
        }
        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            document.write(os);
            return os;
        }

    }

    private static void replaceInParagraph(XWPFParagraph xwpfParagraph, String oldString, String newString) {
        List<XWPFRun> runs = xwpfParagraph.getRuns();
        int runSize = runs.size();
        StringBuilder textSb = new StringBuilder();
        Map<Integer, String> textMap = new HashMap<>();
        for (int j = 0; j < runSize; j++) {
            XWPFRun xwpfRun = runs.get(j);
            Integer textPosition = xwpfRun.getTextPosition();
            String text = xwpfRun.getText(textPosition);
            textSb.append(text);
            textMap.put(j, text);
        }
        if (!textSb.toString().contains(oldString)) return;

        int startIndex = 0;
        int mapSize = textMap.size();
        int maxEndIndex = oldString.length();
        Integer startPosition = null, endPosition = null;
        String uuid = UUID.randomUUID().toString();
        alwaysFor:
        for (; ; ) {
            if (startIndex > mapSize) break;
            int endIndex = startIndex;
            while (endIndex >= startIndex && maxEndIndex > endIndex - startIndex) {
                StringBuilder strSb = new StringBuilder();
                for (int i = startIndex; i <= endIndex; i++)
                    strSb.append(
                            textMap.getOrDefault(i, uuid));
                if (!strSb.toString().trim().equals(oldString)) ++endIndex;
                else {
                    startPosition = startIndex;
                    endPosition = endIndex;
                    break alwaysFor;
                }
            }
            ++startIndex;
        }

        if (startPosition != null && endPosition != null) {
            XWPFRun modelRun = runs.get(endPosition);
            XWPFRun xwpfRun = xwpfParagraph.insertNewRun(endPosition + 1);
            xwpfRun.setText(newString);
            if (modelRun.getFontSize() != -1) xwpfRun.setFontSize(modelRun.getFontSize());
            xwpfRun.setFontFamily(modelRun.getFontFamily());
            for (int i = endPosition; i >= startPosition; i--) xwpfParagraph.removeRun(i);
        } else {
            String text = xwpfParagraph.getText();
            XWPFRun xwpfRun = xwpfParagraph.getRuns().get(0);
            String fontFamily = xwpfRun.getFontFamily();
            int fontSize = xwpfRun.getFontSize();
            XWPFRun insertXwpfRun = xwpfParagraph.insertNewRun(runSize);
            insertXwpfRun.setText(text.replace(oldString, newString));
            insertXwpfRun.setFontFamily(fontFamily);
            insertXwpfRun.setFontSize(fontSize);
            for (int i = runSize - 1; i >= 0; i--) xwpfParagraph.removeRun(i);
        }
    }


    /**
     * word插入表格
     *
     * @param doc         装载前的doc
     * @param resultList  装载的数据
     * @param tblIndex    装载第几个table,0为第一个
     * @param mdlRowIndex 模板行的下标，1为第二行
     * @param isDelTmpRow 是否删除模板行
     * @throws Exception
     */
    public static void insertTable(XWPFDocument doc, List<List<String>> resultList, int tblIndex, int mdlRowIndex, boolean isDelTmpRow) {
        List<XWPFTable> tableList = doc.getTables();
        XWPFTable table = tableList.get(tblIndex);
        insertValueToTable(table, resultList, mdlRowIndex, isDelTmpRow);
    }

    private static void insertValueToTable(XWPFTable table, List<List<String>> resultList, int moduleRowIndex, boolean isDelTmpRow) {
        List<XWPFTableRow> rows = table.getRows();
        List<XWPFTableCell> cells = null;
        List<XWPFTableCell> tmpCells = null;//模版列
        XWPFTableRow tmpRow = null;//匹配用
        XWPFTableCell tmpCell = null;//匹配用
        if (rows.size() > 0) {
            tmpRow = rows.get(moduleRowIndex);
            if (tmpRow.getTableCells().size() != resultList.get(0).size()) {
                throw new RuntimeException("传入list<list<String>>中的list<String>size与的docx模板 列数不吻合");
            }
        } else {
            throw new RuntimeException("docx模板没有表格模板");
        }
        tmpCells = tmpRow.getTableCells();
        for (int i = 0, len = resultList.size(); i < len; i++) {
            XWPFTableRow row = table.createRow();
            row.setHeight(tmpRow.getHeight());
            List<String> list = resultList.get(i);
            cells = row.getTableCells();
            //插入的行会填充与表格第一行相同的列数
            for (int k = 0, klen = cells.size(); k < klen; k++) {
                tmpCell = tmpCells.get(k);
                XWPFTableCell cell = cells.get(k);
                setCellText(tmpCell, cell, list.get(k));
            }
            //继续写剩余的列
            for (int j = cells.size(), jlen = list.size(); j < jlen; j++) {
                tmpCell = tmpCells.get(j);
                XWPFTableCell cell = row.addNewTableCell();
                setCellText(tmpCell, cell, list.get(j));
            }
        }
        //删除模版行
        if (isDelTmpRow) {
            table.removeRow(moduleRowIndex);
        }
    }


    private static void setCellText(XWPFTableCell tmpCell, XWPFTableCell cell, String text) {
        CTTc cttc2 = tmpCell.getCTTc();
        CTTcPr ctPr2 = cttc2.getTcPr();

        CTTc cttc = cell.getCTTc();
        CTTcPr ctPr = cttc.addNewTcPr();
        //cell.setColor(tmpCell.getColor());
        //cell.setVerticalAlignment(tmpCell.getVerticalAlignment());
        if (ctPr2.getTcW() != null) {
            ctPr.addNewTcW().setW(ctPr2.getTcW().getW());
        }
        if (ctPr2.getVAlign() != null) {
            ctPr.addNewVAlign().setVal(ctPr2.getVAlign().getVal());
        }
        if (cttc2.getPList().size() > 0) {
            CTP ctp = cttc2.getPList().get(0);
            if (ctp.getPPr() != null) {
                if (ctp.getPPr().getJc() != null) {
                    cttc.getPList().get(0).addNewPPr().addNewJc().setVal(ctp.getPPr().getJc().getVal());
                }
            }
        }

        if (ctPr2.getTcBorders() != null) {
            ctPr.setTcBorders(ctPr2.getTcBorders());
        }

        XWPFParagraph cellP = cell.getParagraphs().get(0);
        XWPFRun cellR = cellP.createRun();

        XWPFParagraph cellP2 = tmpCell.getParagraphs().get(0);
        XWPFRun cellR2;
        if (cellP2.getRuns().size() > 0) {
            cellR2 = cellP2.getRuns().get(0);
            copyRunStyle(cellR2, cellR);
        }

        cellR.setText(text);
    }

    /**
     * 复制runstyle
     * 参数都 不能 为空
     */
    private static XWPFRun copyRunStyle(XWPFRun runFrom, XWPFRun runTo) {
        if (runFrom == null || runTo == null) {
            throw new NullPointerException("xwpfRun 参数不能为空");
        }
        if (-1 == runFrom.getFontSize()) {
            runTo.setFontSize(12);
        } else {
            runTo.setFontSize(runFrom.getFontSize());
        }
        runTo.setColor(runFrom.getColor());
        runTo.setBold(runFrom.isBold());
        runTo.setSubscript(runFrom.getSubscript());
        runTo.setUnderline(runFrom.getUnderline());
        runTo.setFontFamily(runFrom.getFontFamily());
        return runTo;
    }

    /**
     * @description word跨列合并
     */
    public static void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
        for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
            XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
            if (cellIndex == fromCell) {
                // The first merged cell is set with RESTART merge value
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    /**
     * @description word跨行合并
     */
    public static void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
        for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
            XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
            if (rowIndex == fromRow) {
                // The first merged cell is set with RESTART merge value
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    public static void mergeCellsVertically(XWPFTable table, int col) {
        List<XWPFTableRow> rows = table.getRows();
        XWPFTableRow lastRow = rows.get(0);
        for (XWPFTableRow row : rows) {
            if (row == lastRow) {
                continue;
            }
            XWPFTableCell lastRowCell = lastRow.getCell(col);
            XWPFTableCell cell = row.getCell(col);
            if (!isEmpty(lastRowCell.getText()) && lastRowCell.getText().equals(cell.getText())) {
                if (!lastRowCell.getCTTc().getTcPr().isSetCellMerge()) {
                    lastRowCell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
                }
                if (!cell.getCTTc().getTcPr().isSetCellMerge()) {
                    cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
                }
            } else {
                lastRow = row;
            }
        }
    }

    private static boolean isEmpty(final CharSequence cs) {
        return cs == null || cs.length() == 0;
    }
    
    private boolean isBlank(final CharSequence cs) {
        final int strLen = cs == null ? 0 : cs.length();
        if (strLen == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(cs.charAt(i))) {
                return false;
            }
        }
        return true;
    }


    public static void main(String[] args) throws IOException {
        String path = "C:\\Users\\zx\\Documents\\WXWork\\1688855678357577\\Cache\\File\\2022-07\\完成总结ljw(1).doc";
        String result = readDoc(path);
        System.out.println(result);
    }

}